import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.concurrent.locks.Lock;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;

@SuppressWarnings("serial")
public class MainPanel extends JPanel implements MouseListener {
	Statistic statisticPanel;
	ComponentPanel componentpanel[][] ;
	Component component;
	
	Police police[];
	PoliceStation polStation ;
	static Racer racer[];
	static int polNum,racNum,tarNum,hidNum,polStationNum;
	static Thread threadPolice[];
	static Thread threadRacer[];
	static Thread threadHidden[];
	JPanel p;
	int componentPanelSize;
	public TemporalDifference temporal;
	Lock lock;
	public MainPanel(int componentPanelSize,Lock lock,Statistic statistic,JPanel p){
		statisticPanel = statistic;
		this.p = p;
		polNum = 0;racNum = 0;tarNum = 1;hidNum = 0;polStationNum = 0;
		polStation = null;
		temporal = defaultTD();
		component = new Empty();
		this.lock = lock;
		police = new Police[3];
		racer = new Racer[3];
		threadPolice = new Thread[3];
		threadRacer = new Thread[3];
		threadHidden = new Thread[4];
		this.componentPanelSize = componentPanelSize;
		
		componentpanel = new ComponentPanel[componentPanelSize][componentPanelSize];
		setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
		setLayout(new GridLayout(componentPanelSize,componentPanelSize,0,0));
		addComponentPanel();
	}
	//adds components to mainpanel
	public void addComponentPanel(){
		for(int i=0; i<componentPanelSize; i++)
			for(int j=0; j<componentPanelSize; j++){
		        
				componentpanel[i][j]= new ComponentPanel(componentPanelSize);
				componentpanel[i][j].addMouseListener(this);
				add(componentpanel[i][j]);
			}
	}
	//determines who won
	public static String whoWon(){
		double average = racer[0].average;
		int winner = racer[0].componentNum;
		String statistics = "Racer : ";
		statistics+=winner;
		statistics+=" average = ";
		statistics+=average;
		statistics+="\n";
		for(int i=1; i<3; i++){
			if(racer[i]!=null){
				if(racer[i].average < average){
					average = racer[i].average;
					winner = racer[i].componentNum;
					
				}
			
				statistics+="Racer : ";
				statistics+=racer[i].componentNum;
				statistics+=" average = ";
				statistics+=racer[i].average;
				statistics+="\n";
			}
		}
		if(MainPanel.racNum>1){
			statistics+="Racer : ";
			statistics+=winner;
			statistics+=" average = ";
			statistics+=average;
			statistics+="  won the game\n";
		}
		return statistics;
	}
	//starts threads
	public void moveComponent(){
		for(int i=0; i<3; i++){
			if(threadPolice[i]!=null){
				threadPolice[i].start();
			}
			if(threadRacer[i]!=null)
				threadRacer[i].start();
		}
		for(short i = 0;i<4;i++)
			if(threadHidden[i]!=null)
			threadHidden[i].start();
	}
	//pauses all running threads
	@SuppressWarnings("deprecation")
	public static void pauseComponent(){
		for(int i=0; i<3; i++){
			if(threadPolice[i]!=null)
				threadPolice[i].suspend();
			if(threadRacer[i]!=null)
				threadRacer[i].suspend();
		}
		for(short i = 0;i<4;i++)
			if(threadHidden[i]!=null)
				threadHidden[i].suspend();
	}
	//kills all thread which is alive
	@SuppressWarnings("deprecation")
	public static void stopComponent(){
		for(int i=0; i<3; i++){
			if(threadPolice[i]!=null)
				threadPolice[i].stop();
			if(threadRacer[i]!=null)
				threadRacer[i].stop();
		}
		for(short i = 0;i<4;i++)
			if(threadHidden[i]!=null)
				threadHidden[i].stop();
			
	}

	@SuppressWarnings("deprecation")
	public void resumeComponent() {
		for(int i=0; i<3; i++){
			if(threadPolice[i]!=null){
				threadPolice[i].resume();
				if(!threadPolice[i].isAlive())
					threadPolice[i].start();
			}
			if(threadRacer[i]!=null){
				threadRacer[i].resume();
				if(!threadRacer[i].isAlive())
					threadRacer[i].start();
			}
			
		}
		for(short i = 0;i<4;i++){
			if(threadHidden[i]!=null){
				threadHidden[i].resume();
				if(!threadHidden[i].isAlive())
					threadHidden[i].start();
			}
		}
	}
	public void removeMouseListeners(){
		for(int i = 0;i<componentPanelSize;i++)
			for(int j = 0;j<componentPanelSize;j++)
				componentpanel[i][j].removeMouseListener(this);
	}
	public void addMouseListeners() {
		// TODO Auto-generated method stub
		for(int i = 0;i<componentPanelSize;i++)
			for(int j = 0;j<componentPanelSize;j++)
				componentpanel[i][j].addMouseListener(this);
	}
	public void resetQValues(){
		for(int i = 0;i<3;i++)
			if(racer[i]!=null)
				for(int j = 0;j<componentPanelSize;j++)
					for(int h = 0;h<componentPanelSize;h++)
						for(int k = 0;k<4;k++){
							racer[i].qTargetValues[j][h][k] = 0;
							racer[i].qHiddenValues[j][h][k] = 0;
						}
		if(polStation!=null)
			for(int j = 0;j<componentPanelSize;j++)
				for(int h = 0;h<componentPanelSize;h++)
					for(int k = 0;k<4;k++)
						polStation.qValues[j][h][k] = 0;
					
	}
	
	public void mouseReleased(MouseEvent arg0) {}
	public void mouseClicked(MouseEvent arg0) {}
	public void mouseEntered(MouseEvent arg0) {}
	public void mouseExited(MouseEvent arg0) {}
	
	public void mousePressed(MouseEvent arg0) {
		
		for(int i=0; i<componentPanelSize; i++)
			for(int j=0; j<componentPanelSize; j++){
				if(arg0.getSource()==componentpanel[i][j]){
					
					if(component instanceof Empty){
						boolean ispolStation = false;
						if(!(componentpanel[i][j].component instanceof Empty) || componentpanel[i][j].isHidden==true)
							ispolStation = reCalculateComponentNum(componentpanel[i][j]);
						if(!ispolStation){
							componentpanel[i][j].component = new Empty();
							componentpanel[i][j].putComponent(component.getImage());
						}
					}
					else if(component instanceof Police && polNum<3){
						if(polStationNum!=1){
							try{
								JOptionPane.showMessageDialog(null,"You can not add police before adding police station");
							}
						
							catch(Exception ex){
								ex.printStackTrace();
							}
						}
						else{
							if(police[polNum]==null){
								boolean ispolStation = false;
								if((!(componentpanel[i][j].component instanceof Empty) || componentpanel[i][j].isHidden==true)){
									ispolStation = reCalculateComponentNum(componentpanel[i][j]);
									if(!ispolStation  && componentpanel[i][j].component instanceof PoliceStation){
										polStationNum++;
										ispolStation = true;
									}
								}
								if(!ispolStation){
									createPolice(i,j);
								}
							}
						}
					}
				     
					else if(component instanceof Racer && racNum<3){
				    	 if(racer[racNum]==null){
				    		 boolean ispolStation = false;
				    		 if(!(componentpanel[i][j].component instanceof Empty) || componentpanel[i][j].isHidden==true)
				    			 ispolStation = reCalculateComponentNum(componentpanel[i][j]);
				    		 if(!ispolStation){
				    			createRacer(i,j);
				    	 	}
				    	 }
				     }
					else if(component instanceof PoliceStation && polStationNum<1){
				    	 
				    	 if(!(componentpanel[i][j].component instanceof Empty) || componentpanel[i][j].isHidden==true)
								reCalculateComponentNum(componentpanel[i][j]);
				    	 createPoliceStation(i,j);
				     }
					else if(component instanceof Obstacle){
				    	 boolean ispolStation = false;
				    	 if(!(componentpanel[i][j].component instanceof Empty) || componentpanel[i][j].isHidden==true)
				    		 ispolStation = reCalculateComponentNum(componentpanel[i][j]);
				    		 
				    	 if(!ispolStation){
				    		 createObstacle(i,j);
				    	 }
				     }
				     
					else if(component instanceof Target && tarNum==1){
				    	 boolean ispolStation = false;
				    	 if(!(componentpanel[i][j].component instanceof Empty) || componentpanel[i][j].isHidden==true)
				    		 
				    		 ispolStation = reCalculateComponentNum(componentpanel[i][j]);
				    	 
				    	 if(!ispolStation){
				    		 createTarget(i,j);
				         }
				     }
				     
					else if(component== null && hidNum<4){
				    	 if(threadHidden[hidNum]==null){
				    		 boolean ispolStation = false;
				    		 if(!(componentpanel[i][j].component instanceof Empty))
				    			 ispolStation = reCalculateComponentNum(componentpanel[i][j]);	
				    		 if(!ispolStation){
				    			 createHidden(i,j);
				    		 }
				    	 }
				    	 else
				    		 hidNum++;
				     }
				  }
				}
			}
	//recalculates component counts in main panel and return true if changing object is PoliceStation,
	//when there is already one or more police in the panel 
	private boolean reCalculateComponentNum(ComponentPanel c) {
		if(c.component instanceof Police){
			polNum = ((Police)c.component).componentNum -1;
			police[polNum] = null;
			threadPolice[polNum] = null;
			for(int i = 0;i<3;i++)
				if(police[i]==null){
					polNum = i;
					break;
				}	
		}
		else if(c.component instanceof Racer){
			racNum = ((Racer)c.component).componentNum -1;
			racer[racNum] = null;
			threadRacer[racNum] = null;
			for(int i = 0;i<3;i++)
				if(racer[i]==null){
					racNum = i;
					break;
				}	
		}
		else if(c.component instanceof PoliceStation){
			for(int i = 0;i<3;i++)
				if(police[i]!=null)
					return true;
			polStationNum--;
		}
		else if(c.component instanceof Target)
			tarNum = 1;
		else if(c.isHidden==true){
			hidNum = c.componentNum;
			threadHidden[hidNum] = null;
			for(int i = 0;i<4;i++)
				if(threadHidden[i]==null){
					hidNum = i;
					break;
				}	
			c.setBorder(null);
			c.isHidden = false;
		}
		return false;
	}
	//creates hidden place
	public void createHidden(int i,int j){
		componentpanel[i][j].isHidden = true;
		componentpanel[i][j].setBorder(new LineBorder(Color.red,1));
		componentpanel[i][j].componentNum = hidNum;
		threadHidden[hidNum] = new Thread(new HiddenFlash(componentpanel[i][j]));
		hidNum++;
	}
	public void createObstacle(int i,int j){
		componentpanel[i][j].component = new Obstacle();
		componentpanel[i][j].putComponent(componentpanel[i][j].component.getImage());
	}
	public void createPoliceStation(int i,int j){
		polStation = new PoliceStation(componentPanelSize);
   	 	componentpanel[i][j].component = polStation;
        componentpanel[i][j].putComponent(polStation.getImage());
        polStation.temporal = new Sarsa(temporal.alpha,temporal.gamma,temporal.epsilon,0); 
        polStationNum++;
	}
	public void createPolice(int i,int j){
		police[polNum] = new Police(componentPanelSize,polNum);
		police[polNum].initialState = new State(i,j);
		police[polNum].currentState = new State(i,j);
		componentpanel[i][j].component = police[polNum];
		componentpanel[i][j].putComponent(police[polNum].getImage());
		componentpanel[i][j].isNextPolice = true;
		threadPolice[polNum] = new Thread(new MovePolice(police[polNum],componentpanel,componentPanelSize,lock,polStation));
		polNum++;
	}
	public void createRacer(int i,int j){
		
		racer[racNum] = new Racer(componentPanelSize,racNum);
		racer[racNum].currentState = new State(i,j);
		racer[racNum].initialState = new State(i,j);
		
		racer[racNum].temporal = new DynaQ(temporal.alpha,temporal.gamma,temporal.epsilon,0,10);	 
		componentpanel[i][j].component = racer[racNum];
		componentpanel[i][j].putComponent(racer[racNum].getImage());
		threadRacer[racNum] = new Thread(new MoveRacer(racer[racNum],componentpanel,componentPanelSize,lock,statisticPanel,p));
		racNum++;
	}
	public void createTarget(int i ,int j){
		componentpanel[i][j].component = new Target();
		 componentpanel[i][j].putComponent(componentpanel[i][j].component.getImage());
		 componentpanel[i][j].component.initialState = new State(i,j);
		 componentpanel[i][j].component.currentState = new State(i,j);
		 tarNum=0;
	}
	public void loadDefaultMapUpper() {
		// TODO Auto-generated method stub
		createRacer(0,0);
		createPoliceStation(componentPanelSize-1,0);
		createPolice(0,componentPanelSize-1);
		createTarget(componentPanelSize-1,componentPanelSize-1);
		for(int i = 0;i<componentPanelSize/2-1;i++)
			createObstacle(componentPanelSize/2,i);
		for(int i = componentPanelSize/2 + 1;i<componentPanelSize;i++)
			createObstacle(componentPanelSize/2,i);
		createHidden(2,componentPanelSize/2-1);
		createHidden(2,componentPanelSize/2);
		createHidden(1,componentPanelSize/2 -1);
		createHidden(1,componentPanelSize/2 );
	}
	public void loadDefaultMapLower() {
		// TODO Auto-generated method stub
		createRacer(0,0);
		createPoliceStation(componentPanelSize-1,0);
		createPolice(0,componentPanelSize-1);
		createTarget(componentPanelSize-1,componentPanelSize-1);
		for(int i = 0;i<componentPanelSize/2-1;i++)
			createObstacle(componentPanelSize/2,i);
		for(int i = componentPanelSize/2 + 1;i<componentPanelSize;i++)
			createObstacle(componentPanelSize/2,i);
		createHidden(10,componentPanelSize/2-1);
		createHidden(10,componentPanelSize/2);
		createHidden(9,componentPanelSize/2 -1);
		createHidden(9,componentPanelSize/2 );
	}
	public void loadDefaultMap() {
		// TODO Auto-generated method stub
		createRacer(0,0);
		createPoliceStation(componentPanelSize-1,0);
		createPolice(0,componentPanelSize-1);
		createTarget(componentPanelSize-1,componentPanelSize-1);
		for(int i = 0;i<componentPanelSize/2-1;i++)
			createObstacle(componentPanelSize/2,i);
		for(int i = componentPanelSize/2 + 1;i<componentPanelSize;i++)
			createObstacle(componentPanelSize/2,i);
	}
	public void loadDefaultMapWithNoPolice() {
		// TODO Auto-generated method stub
		createRacer(0,0);
		createTarget(componentPanelSize-1,componentPanelSize-1);
		for(int i = 0;i<componentPanelSize/2-1;i++)
			createObstacle(componentPanelSize/2,i);
		for(int i = componentPanelSize/2 + 1;i<componentPanelSize;i++)
			createObstacle(componentPanelSize/2,i);
	}
	//default temporal diffrence of the panel
	public Sarsa defaultTD(){
		return new Sarsa(0.5,0.5,0.1,0);
	}
class HiddenFlash implements Runnable{
	
		boolean isHidden = true;
		ComponentPanel c;
		public HiddenFlash(ComponentPanel c){	
			this.c = c;
		}
		
		public void run(){
		
			try {
				
				while(true){
					Thread.sleep(500);
					if(isHidden){
						c.putComponent(new ImageIcon("picture/free.png").getImage());
					}
					else {
						c.putComponent(c.component.getImage());
						
					}
					isHidden = (!isHidden);
				}
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}
}